﻿// -------------------------------------------------------------------------------------------------------
// LICENSE INFORMATION
//
// - This software is licensed under the MIT shared source license.
// - The "official" source code for this project is maintained at http://oncfext.codeplex.com
//
// Copyright (c) 2010 OpenNETCF Consulting
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
// -------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace OpenNETCF.Web
{
    public class RestConnector
    {
        private object m_syncRoot = new object();
        private string m_address;

        public string DeviceAddress 
        {
            get { return m_address; }
            set
            {
                var address = value;
                if (!address.StartsWith("http", StringComparison.CurrentCultureIgnoreCase))
                {
                    address = "http://" + address;
                }

                var uri = new Uri(address, UriKind.Absolute);
                m_address = string.Format("{0}://{1}:{2}", uri.Scheme, uri.Host, uri.Port);
            }
        }

        public RestConnector(string deviceAddress)
        {
#if!(WindowsCE || WINDOWS_PHONE || ANDROID || MONOTOUCH)
            System.Net.Cache.HttpRequestCachePolicy policy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.NoCacheNoStore); 
            HttpWebRequest.DefaultCachePolicy = policy; 
#endif
            DeviceAddress = deviceAddress;
        }

        protected virtual CredentialCache GenerateCredentials()
        {
            return null;
        }

        public string Get(string directory)
        {
                return Get(directory, Timeout.Infinite);
        }

        public string Get(string directory, int timeout)
        {
            if (!Monitor.TryEnter(m_syncRoot))
            {
                Debug.WriteLine("RestConnector Get can't get lock");
                return string.Empty;
            }
            try
            {
                directory = directory.Replace('\\', '/');
                if (!directory.StartsWith("/"))
                {
                    directory = "/" + directory;
                }

                string page = string.Format("{0}{1}", DeviceAddress, directory);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(page);

                CredentialCache creds = GenerateCredentials();
                if (creds != null)
                {
                    request.Credentials = creds;
                }
#if !WINDOWS_PHONE
                request.KeepAlive = false;

                if (timeout > 0)
                {
                    request.ReadWriteTimeout = timeout;
                    request.Timeout = timeout;
                }
#endif
                try
                {
                    using (var response = (HttpWebResponse)request.GetResponse())
                    {
                        return GetResponseData(response);
                    }

                    //var response = (HttpWebResponse)request.GetResponse();
                    //return GetResponseData(response);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    request.Abort();
                    return string.Empty;
                }
            }
            finally
            {
                Monitor.Exit(m_syncRoot);
            }
        }

        private string SendData(string method, string directory, string data)
        {
            return SendData(method, directory, data, Timeout.Infinite);
        }

        private string SendData(string method, string directory, string data, int timeout)
        {
            lock (m_syncRoot)
            {
                directory = directory.Replace('\\', '/');
                if (!directory.StartsWith("/"))
                {
                    directory = "/" + directory;
                }

                string page = string.Format("{0}{1}", DeviceAddress, directory);

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(page);
#if !WINDOWS_PHONE
                request.KeepAlive = false;
                request.ProtocolVersion = HttpVersion.Version10;
#endif
                request.Method = method;

                CredentialCache creds = GenerateCredentials();
                if (creds != null)
                {
                    request.Credentials = creds;
                }

                // turn our request string into a byte stream
                byte[] postBytes;

                if (data != null)
                {
                    postBytes = Encoding.UTF8.GetBytes(data);
                }
                else
                {
                    postBytes = new byte[0];
                }

#if !WINDOWS_PHONE
                if (timeout < 0)
                {
                    request.ReadWriteTimeout = timeout;
                    request.Timeout = timeout;
                }

                request.ContentLength = postBytes.Length;
                request.KeepAlive = false;
#endif
                request.ContentType = "application/x-www-form-urlencoded";

                try
                {
                    Stream requestStream = request.GetRequestStream();

                    // now send it
                    requestStream.Write(postBytes, 0, postBytes.Length);
                    requestStream.Close();


                    using (var response = (HttpWebResponse)request.GetResponse())
                    {
                        return GetResponseData(response);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                    request.Abort();
                    return string.Empty;
                }
            }
        }

        public string Post(string directory, string data, int timeout)
        {
            return SendData("POST", directory, data, timeout);
        }

        public string Post(string directory, string data)
        {
            return SendData("POST", directory, data);
        }

        public string Put(string directory, string data, int timeout)
        {
            return SendData("PUT", directory, data, timeout);
        }

        public string Put(string directory, string data)
        {
            return SendData("PUT", directory, data);
        }

        public string Delete(string directory, int timeout)
        {
            return SendData("DELETE", directory, null, timeout);
        }

        public string Delete(string directory)
        {
            return SendData("DELETE", directory, null);
        }

        private string GetResponseData(HttpWebResponse response)
        {
            StringBuilder sb = new StringBuilder();

            byte[] buf = new byte[8192];

            using (var stream = response.GetResponseStream())
            {
                string result = null;
                int count = 0;

                do
                {
                    count = stream.Read(buf, 0, buf.Length);

                    if (count != 0)
                    {
                        // look for a UTF8 header
                        if ((buf[0] == 0xEF) && (buf[1] == 0xBB) && (buf[2] == 0xBF))
                        {
                            result = Encoding.UTF8.GetString(buf, 3, count - 3);
                        }
                        else
                        {
                            result = Encoding.UTF8.GetString(buf, 0, count);
                        }
                        sb.Append(result);
                    }
                } while (count > 0);
            }
            return sb.ToString();
        }
        
    }
}
